﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using XNAGuiLib.GuiLibAction;
using XNAGuiLib.GuiLibResourceManager;
using Microsoft.Xna.Framework.Content;

namespace XNAGuiLib.GuiLibButtons
{
    public class ButtonObject : InterfaceObject
    {
        // 
        #region Field - Text
        /// <summary>
        /// text to be displayed on the button
        /// </summary>
        protected string _text;
        /// <summary>
        /// gets or sets the Text to display on the Button
        /// </summary>
        public string Text
        {
            get
            {
                return _text;
            }
            set
            {
                _text = value;
                _textObjs[0].Text = value;
            }
        }
        #endregion
        // 
        #region Field - State
        /// <summary>
        /// state of the ButtonObject (see enumeration ButtonObjectState for details)
        /// </summary>
        protected ButtonObjectState _state;
        /// <summary>
        /// gets or sets the ButtonObjectstate for this Button
        /// </summary>
        public ButtonObjectState State
        {
            get
            {
                return _state;
            }
            set
            {
                _state = value;
            }
        }
        #endregion
        //
        #region Field - MidPoint
        /// <summary>
        /// position of the center (vertical and horizontal) of the buttons' surface
        /// </summary>
        protected Vector2 _midPoint;
        /// <summary>
        /// gets or sets the midPoint of this ButtonObject
        /// </summary>
        public Vector2 MidPoint
        {
            get
            {
                return _midPoint;
            }
            set
            {
                _midPoint = value;
            }
        }
        #endregion
        //
        #region Field - TextObj
        /// <summary>
        /// gets or sets the default TextObject (index 0)
        /// </summary>
        public TextObject TextObj
        {
            get
            {
                return _textObjs[0];
            }
            set
            {
                _textObjs[0] = value;
                _textObjs[0].Position = _position;
                _textObjs[0].RelativePosition = _relativePosition;
            }
        }
        #endregion
        //
        #region Field - Rotation
        /// <summary>
        /// gets or sets the InterfaceObjects drawind rotation
        /// </summary>
        public new float Rotation
        {
            get
            {
                return _rotation;
            }
            set
            {
                _rotation = value;

                foreach (TextureObject txo in _textureObjs)
                {
                    txo.Rotation = value;
                }

                foreach (TextObject to in _textObjs)
                {
                    to.Rotation = value;
                }

                // need to reset Position and Bounds
            }
        }
        #endregion
        //
        #region Field - Hover
        /// <summary>
        /// gets or sets the 'Hover' TextureObject of this button
        /// </summary>
        public TextureObject Hover
        {
            get
            {
                // returns the third object in the textureobject list
                if (_textureObjs.Count >= 3)
                    return _textureObjs[2];
                else return null;
            }
            set
            {
                // set the third object in the textureobject list to 'value'
                _textureObjs[2] = value;
                // set the third object's position to the button's position
                _textureObjs[2].Position = _position;
                // set the third object's bounds to the button's bounds
                _textureObjs[2].Bounds = _bounds;
            }
        }
        #endregion
        //
        #region Field - Clicked
        /// <summary>
        /// gets or sets the 'Clicked' TextureObject of this button
        /// </summary>
        public TextureObject Clicked
        {
            get
            {
                // returns the second object in the textureobject list
                if (_textureObjs.Count >= 2)
                    return _textureObjs[1];
                else return null;
            }
            set
            {
                // set the second object in the textureobject list to 'value'
                _textureObjs[1] = value;
                // set the second object's position to the button's position
                _textureObjs[1].Position = _position;
                // set the second object's bounds to the button's bounds
                _textureObjs[1].Bounds = _bounds;
            }
        }
        #endregion
        //
        #region Field - Unclicked
        /// <summary>
        /// gets or sets the 'Unclicked' TextureObject of this button
        /// </summary>
        public TextureObject Unclicked
        {
            get
            {
                // returns the first object in the textureobject list
                if (_textureObjs.Count >= 1)
                    return _textureObjs[0];
                else return null;
            }
            set
            {
                // set the first object in the textureobject list to 'value'
                _textureObjs[0] = value;
                // set the first object's position to the button's position
                _textureObjs[0].Position = _position;
                // set the first object's bounds to the button's bounds
                _textureObjs[0].Bounds = _bounds;
            }
        }
        #endregion
        //
        #region Field - MouseState
        // MouseState of current cycle
        protected MouseState _ms;
        /// <summary>
        /// gets the current MouseState used by this ButtonObject
        /// </summary>
        public MouseState MouseState
        {
            get
            {
                return _ms;
            }
        }
        #endregion
        //
        #region Field - OldMouseState
        // old MouseState (from last update cycle)
        protected MouseState _oms;
        /// <summary>
        /// gets the old MouseState used by this ButtonObject
        /// </summary>
        public MouseState OldMouseState
        {
            get
            {
                return _oms;
            }
        }
        #endregion
        //
        #region Field - ButtonSize
        /// <summary>
        /// button size (for determining the best to use resources|see enumeration ButtonSize for details)
        /// </summary>
        protected ButtonSize _buttonSize;
        /// <summary>
        /// gets the ButtonSize or this Button
        /// </summary>
        public ButtonSize BtnSize
        {
            get
            {
                // returns the button's buttonsize field
                return _buttonSize;
            }
        }
        #endregion
        //
        #region Field - TextAlign
        /// <summary>
        ///
        /// </summary>
        protected ButtonTextAlign _textAlign = ButtonTextAlign.Center;
        /// <summary>
        /// 
        /// </summary>
        public ButtonTextAlign TextAlign
        {
            get
            {
                return _textAlign;
            }
            set
            {
                _textAlign = value;
                ResetText();
            }
        }
        #endregion
        //
        //
        #region Enumeration - ButtonObjectState
        /// <summary>
        /// enumeration for the button state (used to determine how the button is drawn)
        /// </summary>
        public enum ButtonObjectState
        {
            /// <summary>
            /// 'Clicked' state using the '_clicked'-resource
            /// </summary>
            Clicked,
            /// <summary>
            /// 'Hover' state using the '_hover'-resource
            /// </summary>
            Hover,
            /// <summary>
            /// 'Unclicked' state using the '_unclicked'-resource
            /// </summary>
            Unclicked
        }
        #endregion
        //
        #region Enumeration - ButtonSize
        /// <summary>
        /// enumeration for button sizes describing possible (optimized) bounds for a button and the resources used
        /// </summary>
        public enum ButtonSize
        {
            /// <summary>
            /// button size of 17 x 17 pixels
            /// </summary>
            BS017x017,
            /// <summary>
            /// button size of 67 x 17 pixels
            /// </summary>
            BS067x017,
            /// <summary>
            /// button size of 134 x 17 pixels
            /// </summary>
            BS134x017
        }
        #endregion
        //
        #region Enumeration - ButtonTextAlign
        public enum ButtonTextAlign
        {
            Left,
            Center,
            Right
        }
        #endregion
        //
        //
        #region Constructor - ButtonObject(Game game, string name, Vector2 relPosition, Vector2 bounds, string text, ButtonObjectState state, TextureObject unclicked,  TextureObject clicked)
        public ButtonObject(Game game, string name, Vector2 relPosition, Vector2 bounds, string text, ButtonObjectState state) 
            : base(game, name, relPosition, true)
        {
            // set (up) base fields
            #region Init - Base Fields
            // set Enabling default to true
            Enabled = true;
            // set the name
            _name = name;
            // set the Position
            _relativePosition = relPosition;
            // set the ButtonState
            _state = state;
            // set the Bounds
            Bounds = bounds;
            // set the midPoint Field
            _midPoint = new Vector2(_bounds.X / 2, _bounds.Y / 2);
            // set the initial TextObject
            _font = GuiResourceManager.Instance(this.Game).FontManager.Load<SpriteFont>("DefaultButton");
            #endregion
            // set (up) TextObject for this button
            #region Init - Text
            // Calculating Textposition on the Center of the Button 
            // (i don't think there are buttons where text ist algined left or right)
            // but nevertheless it will perhabs be implemented later on
            Vector2 textSize = _font.MeasureString(text);
            Vector2 textHalfSize = textSize / 2;
            Vector2 textRelPosition = _midPoint - textHalfSize;
            // set the TextObject
            _text = text;
            _textObjs.Add(new TextObject(text, _font, this.Name + "_text", textRelPosition, Statics.ZeroVector2));
            #endregion
            // set (up) TextureObjects for this button (add 3 empty objects to the list)
            #region Init - TextureObjects
            // set the Clicked/Unclicked/Hover TextureObject
            _textureObjs = new List<TextureObject>();
            _textureObjs.Add(new TextureObject());
            _textureObjs.Add(new TextureObject());
            _textureObjs.Add(new TextureObject());
            #endregion
        }
        #endregion
        //
        //
        #region Private Method - ResetText()
        protected void ResetText()
        {
            _textObjs[0].Text = _text;

            Vector2 textSize;
            Vector2 textHalfSize;
            Vector2 textRelPosition;

            switch (_textAlign)
            {
                case ButtonTextAlign.Center:
                    _midPoint = new Vector2(_bounds.X / 2, _bounds.Y / 2);

                    textSize = _font.MeasureString(_text);
                    textHalfSize = textSize / 2;
                    textRelPosition = _midPoint - textHalfSize;
                    _textObjs[0].RelativePosition = textRelPosition;
                    break;

                case ButtonTextAlign.Left:
                    _midPoint = new Vector2(2, _bounds.Y / 2);

                    textSize = _font.MeasureString(_text);
                    textHalfSize = new Vector2(0, textSize.Y / 2);
                    textRelPosition = _midPoint - textHalfSize;
                    _textObjs[0].RelativePosition = textRelPosition;
                    break;

                case ButtonTextAlign.Right:
                    _midPoint = new Vector2(_bounds.X - 2, _bounds.Y / 2);

                    textSize = _font.MeasureString(_text);
                    textHalfSize = new Vector2(textSize.X, textSize.Y / 2);
                    textRelPosition = _midPoint - textHalfSize;
                    _textObjs[0].RelativePosition = textRelPosition;
                    break;
            }
        }
        #endregion
        //
        //
        #region Override Method - Rescale(Vector2 bounds)
        public override void Rescale(Vector2 bounds)
        {
            _bounds = bounds;
            // Rescale Hover
            if (this.Hover != null)
            {
                this.TextureObject(this._textureObjs.IndexOf(this.Hover)).Bounds = _bounds;
                this.TextureObject(this._textureObjs.IndexOf(this.Hover)).RelativePosition = new Vector2(0, 0);
            }
            // Rescale Clicked
            if (this.Clicked != null)
            {
                this.TextureObject(this._textureObjs.IndexOf(this.Clicked)).Bounds = _bounds;
                this.TextureObject(this._textureObjs.IndexOf(this.Clicked)).RelativePosition = new Vector2(0, 0);
            }
            // Rescale Unclicked
            if (this.Unclicked != null)
            {
                this.TextureObject(this._textureObjs.IndexOf(this.Unclicked)).Bounds = _bounds;
                this.TextureObject(this._textureObjs.IndexOf(this.Unclicked)).RelativePosition = new Vector2(0, 0);
            }
            // Reposition TextObject
            ResetText();
        }
        #endregion
        //
        #region Override Method - ExecuteCommand(string command)
        public override void ExecuteCommand(string command)
        {
            // Command for releasing the Button (spring button behavior)
            #region Command - 'ShowUnclicked'
            if (command.Equals("showUnclicked"))
            {
                // check if the first texture object (unclicked) is invisible
                if (!_textureObjs[0].Visible)
                {
                    // control console output
                    // Console.WriteLine("Show Unclicked");
                    // set the 'unclicked' texture object to visible
                    _textureObjs[0].Visible = true;
                    // set the 'clicked' texture object to invisible
                    _textureObjs[1].Visible = false;
                }
            }
            #endregion
            // Command for pressing Button (spring button behavior)
            #region Command - 'ShowClicked'
            else if (command.Equals("showClicked"))
            {
                // check if the second texture object (clicked) is invisible
                if (!_textureObjs[1].Visible)
                {
                    // control console output
                    // Console.WriteLine("Show Clicked");
                    // set the 'unclicked' texture object to invisible
                    _textureObjs[0].Visible = false;
                    // set the 'clicked' texture object to visible
                    _textureObjs[1].Visible = true;
                }
            }
            #endregion
            // Command for toggle Button (On/Off switch behavior)
            #region Command - 'ToggleButton'
            else if (command.Equals("toggleButton"))
            {
                // check if the first texture object (unclicked) is invisible
                if (!_textureObjs[0].Visible)
                {
                    // control console output
                    // Console.WriteLine("Show Unclicked");
                    // set the 'unclicked' texture object to visible
                    _textureObjs[0].Visible = true;
                    // set the 'clicked' texture object to invisible
                    _textureObjs[1].Visible = false;
                }
                // check if the second texture object (clicked) is invisible
                else if (!_textureObjs[1].Visible)
                {
                    // control console output
                    // Console.WriteLine("Show Clicked");
                    // set the 'unclicked' texture object to invisible
                    _textureObjs[0].Visible = false;
                    // set the 'clicked' texture object to visible
                    _textureObjs[1].Visible = true;
                }
            }
            #endregion
        }
        #endregion
        //
        #region Override Method - Draw(GameTime gameTime, SpriteBatch sprite)
        public override void Draw(GameTime gameTime, SpriteBatch sprite)
        {
            if (this.IsInitialized)
            {
                // check which state the button currently is in and draw specific TextureObject
                // state = 'clicked' and draw the 'clicked' texture
                if (_state == ButtonObjectState.Clicked) _textureObjs[1].Draw(sprite);
                // state = 'hover' and draw the 'hover' texture
                else if (_state == ButtonObjectState.Hover) _textureObjs[2].Draw(sprite);
                // state = 'unclicked' and draw the 'unclicked' texture
                else _textureObjs[0].Draw(sprite);
                // loop through all textureobjects
                foreach (TextObject textObj in _textObjs)
                {
                    // textObj.Rotation = MathHelper.ToRadians(0.0f);
                    // draw the text objects which are in the TextObject list
                    textObj.Draw(sprite);
                }
            }
        }
        #endregion
        //
        #region Override Method - Update(GameTime gameTime)
        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            if (this.Hover.IsInitialized() && this.Clicked.IsInitialized() && this.Unclicked.IsInitialized())
                _initialized = true;
            else _initialized = false;

            if (this.IsInitialized)
            {
                // foreach loop to check if bounds have been changed
                #region Foreach - Bounds Alignment
                foreach (TextureObject obj in _textureObjs)
                {
                    // check if bounds of any TextureObject isn't equals to the ButtonObject bounds
                    if (!obj.Bounds.Equals(_bounds))
                    {
                        // foreach loop to align TextureObjects bounds to ButtonObject bounds
                        foreach (TextureObject tobj in _textureObjs)
                        {
                            // set the bounds of the tobj to the buttonObject
                            tobj.Bounds = _bounds;
                        }
                    }
                }
                #endregion
                // check if the button is enabled
                #region Check - Enabled / Button-Cases
                if (Enabled)
                {
                    // get the current mousestate 
                    _ms = Mouse.GetState();

                    // check if the left mouse button is Pressed (down) in this cycle and was released (up) in the last cycle
                    // and whether the mouse cursor position is inside the button's bounds
                    if (_ms.LeftButton == ButtonState.Pressed
                        && _oms.LeftButton == ButtonState.Released
                        && this.TextureObject(this._textureObjs.IndexOf(this.Clicked)).CheckCoordinates(_ms))
                    {
                        // set the ButtonObjectState to Clicked (clicked TextureObject will be drawn)
                        _state = ButtonObjectState.Clicked;
                        // ExecuteCommand("showClicked");
                    }
                    // check if the left mouse button is released (up) in this cycle and was pressed (down) in the last cycle
                    else if (_ms.LeftButton == ButtonState.Released
                            && _oms.LeftButton == ButtonState.Pressed)
                    {
                        // set the ButtonObjectState to Unclicked (unclicked TextureObject will be drawn)
                        _state = ButtonObjectState.Unclicked;
                        // ExecuteCommand("showUnclicked");
                    }
                    // check if the left mouse button is released (up) in this cycle
                    // and whether the mouse cursor position is inside the button's bounds
                    else if (this.TextureObject(this._textureObjs.IndexOf(this.Clicked)).CheckCoordinates(_ms)
                        && _ms.LeftButton == ButtonState.Released)
                    {
                        // set the ButtonObjectState to Hover (hover TextureObject will be drawn)
                        _state = ButtonObjectState.Hover;
                    }
                    // check if the left mouse button is released (up) in this cycle
                    // and whether the mouse cursor position is NOT inside the button's bounds
                    else if (!this.TextureObject(this._textureObjs.IndexOf(this.Clicked)).CheckCoordinates(_ms)
                        && _ms.LeftButton == ButtonState.Released)
                    {
                        // set the ButtonObjectState to Unclicked (unclicked TextureObject will be drawn)
                        _state = ButtonObjectState.Unclicked;
                    }
                    // save the current mouse state (of this cycle) to be the old mouse state (next cycle)
                    _oms = _ms;
                    // loop through all actions on a ButtonObject
                    foreach (Action ia in _actions)
                    {
                        // check if the button is visible
                        if (this.Visible)
                        {
                            // call of the 'check'-method of the button's actions
                            ia.Check(Mouse.GetState(), Keyboard.GetState());
                        }
                    }
                }
                #endregion
            }
        }
        #endregion
    }
}